package com.zhaoyun.ledsdk.serial;

import android.content.Context;
import android.os.SystemClock;
import android.util.Log;

import com.ftdi.j2xx.D2xxManager;
import com.ftdi.j2xx.FT_Device;

import java.text.SimpleDateFormat;
import java.util.Locale;

/**
 * Created by suchengjian on 2017/10/31.
 */

public abstract class J2XXSerialHelper {
    private static final SimpleDateFormat formathms = new SimpleDateFormat("HH:mm:ss", Locale.CHINA);
    private Context context;

    public J2XXSerialHelper(Context context, int index) {
        baudRate = 9600;
        stopBit = 1;
        dataBit = 8;
        parity = 0;
        flowControl = 0;
        portIndex = index;
        this.context = context;
        try {
            ftD2xx = D2xxManager.getInstance(context);
        } catch (D2xxManager.D2xxException e) {
            e.printStackTrace();
        }
    }

    private D2xxManager ftD2xx = null;
    private FT_Device ftDev;

    protected int baudRate; /* baud rate */ /*波特率*/
    protected byte stopBit; /* 1:1stop bits, 2:2 stop bits */ /*停止位*/
    protected byte dataBit; /* 8:8bit, 7: 7bit */ /*数据位*/
    protected byte parity; /* 0: none, 1: odd, 2: even, 3: mark, 4: space */ /*校验位*/
    protected byte flowControl; /* 0:none, 1: CTS/RTS, 2:DTR/DSR, 3:XOFF/XON */ /*流控*/

    private void init() {

    }

    private int DevCount = -1;
    private int currentPortIndex = -1;
    private int portIndex = -1;

    private ReadThread readThread;

    private enum DeviceStatus {
        DEV_NOT_CONNECT,
        DEV_NOT_CONFIG,
        DEV_CONFIG
    }

    private boolean bReadTheadEnable = false;
    private boolean uart_configured = false;


    protected void sendCmd(int numBytes, byte[] buffer) {
        if (DeviceStatus.DEV_CONFIG != checkDevice()) {
            return;
        }
        sendData(numBytes, buffer);
    }

    protected void openSerialPort() {
        if (null == ftDev || !ftDev.isOpen()) {
            Log.e("tag", "onResume - reconnect");
            createDeviceList();
            if (DevCount > 0) {
                connectFunction();
                //setUARTInfoString();
                setConfig(baudRate, dataBit, stopBit, parity, flowControl);
            }
        }else{
//            createDeviceList();
        }
    }

    protected void closeSerialPort() {
        disconnectFunction();
    }

    protected void createDeviceList() {
        int tempDevCount = ftD2xx.createDeviceInfoList(context);
        if (tempDevCount > 0) {
            if (DevCount != tempDevCount) {
                DevCount = tempDevCount;
            }
        } else {
            DevCount = -1;
            currentPortIndex = -1;
        }
        Log.d("tag", "检测到设备数量--> " + DevCount);
    }

    private void sendData(int numBytes, byte[] buffer) {
        if (!ftDev.isOpen()) {
            Log.d("tag", "Device not open!");
            return;
        }
        if (numBytes > 0) {
            ftDev.write(buffer, numBytes);
        }
    }

    private DeviceStatus checkDevice() {
        if (ftDev == null || !ftDev.isOpen()) {
            Log.v("tag", "Need to connect to cable");
            return DeviceStatus.DEV_NOT_CONNECT;
        } else if (!uart_configured) {
            //midToast("CHECK: uart_configured == false", Toast.LENGTH_SHORT);
            Log.v("tag", "Need to configure UART.");
            return DeviceStatus.DEV_NOT_CONFIG;
        }
        return DeviceStatus.DEV_CONFIG;
    }

    private void setConfig(int baud, byte dataBits, byte stopBits, byte parity, byte flowControl) {
        // configure port
        // reset to UART mode for 232 devices
        ftDev.setBitMode((byte) 0, D2xxManager.FT_BITMODE_RESET);
        ftDev.setBaudRate(baud);
        switch (dataBits) {
            case 7:
                dataBits = D2xxManager.FT_DATA_BITS_7;
                break;
            case 8:
                dataBits = D2xxManager.FT_DATA_BITS_8;
                break;
            default:
                dataBits = D2xxManager.FT_DATA_BITS_8;
                break;
        }

        switch (stopBits) {
            case 1:
                stopBits = D2xxManager.FT_STOP_BITS_1;
                break;
            case 2:
                stopBits = D2xxManager.FT_STOP_BITS_2;
                break;
            default:
                stopBits = D2xxManager.FT_STOP_BITS_1;
                break;
        }

        switch (parity) {
            case 0:
                parity = D2xxManager.FT_PARITY_NONE;
                break;
            case 1:
                parity = D2xxManager.FT_PARITY_ODD;
                break;
            case 2:
                parity = D2xxManager.FT_PARITY_EVEN;
                break;
            case 3:
                parity = D2xxManager.FT_PARITY_MARK;
                break;
            case 4:
                parity = D2xxManager.FT_PARITY_SPACE;
                break;
            default:
                parity = D2xxManager.FT_PARITY_NONE;
                break;
        }

        ftDev.setDataCharacteristics(dataBits, stopBits, parity);

        short flowCtrlSetting;
        switch (flowControl) {
            case 0:
                flowCtrlSetting = D2xxManager.FT_FLOW_NONE;
                break;
            case 1:
                flowCtrlSetting = D2xxManager.FT_FLOW_RTS_CTS;
                break;
            case 2:
                flowCtrlSetting = D2xxManager.FT_FLOW_DTR_DSR;
                break;
            case 3:
                flowCtrlSetting = D2xxManager.FT_FLOW_XON_XOFF;
                break;
            default:
                flowCtrlSetting = D2xxManager.FT_FLOW_NONE;
                break;
        }
        final byte XON = 0x11;    /* Resume transmission */
        final byte XOFF = 0x13;    /* Pause transmission */
        ftDev.setFlowControl(flowCtrlSetting, XON, XOFF);
        uart_configured = true;
    }

    private void connectFunction() {
        if (portIndex + 1 > DevCount) {
            portIndex = 0;
        }
        if ((currentPortIndex == portIndex)
                && (ftDev != null)
                && ftDev.isOpen()) {
            //Toast.makeText(global_context,"Port("+portIndex+") is already opened.", Toast.LENGTH_SHORT).show();
            return;
        }
        if (bReadTheadEnable) {
            bReadTheadEnable = false;
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        ftDev = ftD2xx.openByIndex(context, portIndex);
        uart_configured = false;
        if (ftDev == null) {
            Log.v("J2XX", "111 Open port(" + portIndex + ") NG!");
            return;
        }
        if (ftDev.isOpen()) {
            currentPortIndex = portIndex;
            //Toast.makeText(global_context, "open device port(" + portIndex + ") OK", Toast.LENGTH_SHORT).show();
            if (!bReadTheadEnable) {
                bReadTheadEnable = true;
                readThread = new ReadThread();
                readThread.start();
            }
        } else {
            Log.d("J2XX", "222 Open port(" + portIndex + ") NG!");
        }
    }

    private void disconnectFunction() {
        DevCount = -1;
        currentPortIndex = -1;
        bReadTheadEnable = false;
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (ftDev != null) {
            if (ftDev.isOpen()) {
                ftDev.close();
            }
        }
    }

    private class ReadThread extends Thread {
        byte[] buf = new byte[512];
        byte[] rxByteArrayTemp = null;// 临时变量：接收到的字节信息

        @Override
        public void run() {
            super.run();
            while (bReadTheadEnable) {
                Log.d("tag", "reading....");
                int size;
                if (buf == null) {
                    // 线程被中止了
                    return;
                } else {
                    size = ftDev.getQueueStatus();
                }
                if (size > 0) {
                    // 发现有信息后就追加到临时变量
                    if (size > buf.length) {
                        size = buf.length;
                    }
                    ftDev.read(buf, size);
                    rxByteArrayTemp = ArrayAppend(rxByteArrayTemp, buf, size);
                } else {
                    // 这次发现没有信息，如果以前有信息的，那就是我们要的数据
                    if (rxByteArrayTemp != null) {
                        byte[] rxByteArray = ArrayAppend(rxByteArrayTemp, null);
                        rxByteArrayTemp = null;
                        ComBean ComRecData = new ComBean(rxByteArray, rxByteArray.length);
//                        ComRecData.toPrint();
                        onDataReceived(ComRecData);
                    }
                }
                // 每20个毫秒去读取数据
                SystemClock.sleep(200);
            }
        }
    }


    /**
     * 将源数组追加到目标数组
     *
     * @param byte_1
     * @param byte_2
     * @return:<br>返回一个新的数组，包括了原数组1和原数组2
     */
    private byte[] ArrayAppend(byte[] byte_1, byte[] byte_2) {
        // java 合并两个byte数组

        if (byte_1 == null && byte_2 == null) {
            return null;
        } else if (byte_1 == null) {
            byte[] byte_3 = new byte[byte_2.length];
            System.arraycopy(byte_2, 0, byte_3, 0, byte_2.length);
            return byte_3;
            //return byte_2;
        } else if (byte_2 == null) {
            byte[] byte_3 = new byte[byte_1.length];
            System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
            return byte_3;
            //return byte_1;
        } else {
            byte[] byte_3 = new byte[byte_1.length + byte_2.length];
            System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
            System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
            return byte_3;
        }

    }

    /**
     * 将源数组追加到目标数组
     *
     * @param byte_1
     * @param byte_2
     * @param size
     * @return:<br>返回一个新的数组，包括了原数组1和原数组2
     */
    private byte[] ArrayAppend(byte[] byte_1, byte[] byte_2, int size) {
        // java 合并两个byte数组

        if (byte_1 == null && byte_2 == null) {
            return null;
        } else if (byte_1 == null) {
            byte[] byte_3 = new byte[size];
            System.arraycopy(byte_2, 0, byte_3, 0, size);
            return byte_3;
            //return byte_2;
        } else if (byte_2 == null) {
            byte[] byte_3 = new byte[byte_1.length];
            System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
            return byte_3;
            //return byte_1;
        } else {
            byte[] byte_3 = new byte[byte_1.length + size];
            System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
            System.arraycopy(byte_2, 0, byte_3, byte_1.length, size);
            return byte_3;
        }
    }

    protected abstract void onDataReceived(ComBean ComRecData);

    protected abstract void openComPort();

    protected abstract void closeComPort();

}
